/*
 * BlocksFile.cpp
 *
 *  Created on: Sep 18, 2011
 *      Author: kimi
 */
#include "BlocksFile.h"

bool BlocksFile::createNewBlock(Block** block) {
	if (*block != NULL)
		return false;

	// creates and initialize the block
	*block = new Block(blockSize);

	(*block)->setId(blockCount);
	blockCount++;
	// update block count
	_file.seekp(0,ios::beg);
	_file.write(reinterpret_cast<char*>(&blockCount), sizeof(unsigned long));


	// write block into file
	_file.seekp(0,ios::end);

	// write free space
	char* info = (*block)->getRawBlock();
	_file.write(info,blockSize);

	_file.flush();

	return true;
}

bool BlocksFile::getEmptyBlock(Block** block) {
	if (*block != NULL)
		return false;

	unsigned long freeBlockId;
	bool hasFreeBlock = false;

	hasFreeBlock = spaceMgr->get(&freeBlockId);
	if (hasFreeBlock) {
		// reuse space
		if (getBlockById(freeBlockId,block) == false) {
			//error
			throw ios_base::failure("cannot get reusable Block .");
		}
		return true;
	}
	else {
		// creates a new block at the end of the file, by default
		if (createNewBlock(block) == false) {
			// error
			throw ios_base::failure("cannot create new Block.");
		}
		return true;
	}
}

bool BlocksFile::getBlockById(unsigned long id,Block** block) {

	if (*block != NULL) {
		return false;
	}

	unsigned long positionInFile = id*blockSize + sizeof(unsigned long);

//	cout << "position in file: " << positionInFile << endl;

	_file.seekg(0,ios::end);
	if (positionInFile >= _file.tellg()) {
		return false;
	}

	_file.seekg(positionInFile,ios::beg);

	char* serialized = new char[blockSize];
	_file.read(serialized,blockSize);

//	cout << "creando block" <<endl;

	(*block) = new Block(serialized,blockSize);

	if (*block == NULL) {
		return false;
	}

	(*block)->setId(id);

//	cout << "end" << endl;

	return true;
}

bool BlocksFile::BlocksFile::updateBlock(Block *block) {

	if (block == NULL) {
		return false;
	}

	unsigned long positionInFile = block->getId()*blockSize + sizeof(unsigned long);

	_file.seekg(0,ios::end);
	if (positionInFile >= _file.tellg()) {
		return false;
	}

	_file.seekp(positionInFile,ios::beg);

	char* info = block->getRawBlock();
	_file.write(info,blockSize);

	_file.flush();

	return true;
}

bool BlocksFile::deleteBlock(unsigned long id) {
	if (spaceMgr->has(id) == false) {
		return spaceMgr->add(id);
	}

	return true;
}

unsigned long BlocksFile::getBlockSize() {
	return blockSize;
}

BlocksFile::BlocksFile(string xFilePath, unsigned long xBlockSize) {
	// open file
	filePath = xFilePath;
	blockSize = xBlockSize;
	blockCount = 0;

	if (blockSize <= 0)
		throw ios_base::failure("block size must be a positive non-zero value");

	_file.open(filePath.c_str(),ios::out | ios::in | ios::binary);

	if (!_file.is_open())
		_file.open(filePath.c_str(), ios::in | ios::out | ios::trunc | ios::binary);

	//TODO: Mejorar usando el logger.
	if (!_file.is_open())
		throw ios_base::failure("cannot create file at \"" + filePath + "\".");

	_file.seekg(0,ios::end);
	unsigned long t = _file.tellg();
	if (t >= sizeof(unsigned long)) {
		_file.seekp(0,ios::beg);
		_file.read(reinterpret_cast<char*>(&blockCount), sizeof(unsigned long));
	}
	else {
		_file.seekp(0,ios::beg);
		_file.write(reinterpret_cast<char*>(&blockCount), sizeof(unsigned long));
		_file.flush();
	}

	spaceMgr = new EmptySpaceMgr(filePath + ".empty");
}

unsigned long BlocksFile::getBlocksCount() {
	return blockCount;
}

BlocksFile::~BlocksFile() {
	// close file
	_file.flush();
	_file.close();
	delete spaceMgr;
}

